package clients

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"

	"github.com/devops-smartbot/devops-smartbot/services/integration-service/internal/models"
	"go.uber.org/zap"
)

type JiraClient struct {
	config *models.JiraConfig
	client *http.Client
	logger *zap.Logger
}

func NewJiraClient(config *models.JiraConfig, logger *zap.Logger) *JiraClient {
	return &JiraClient{
		config: config,
		client: &http.Client{
			Timeout: 30 * time.Second,
		},
		logger: logger,
	}
}

type jiraIssueRequest struct {
	Fields jiraIssueFields `json:"fields"`
}

type jiraIssueFields struct {
	Project     jiraProject `json:"project"`
	Summary     string      `json:"summary"`
	Description string      `json:"description"`
	IssueType   jiraType    `json:"issuetype"`
	Priority    *jiraPriority `json:"priority,omitempty"`
}

type jiraProject struct {
	Key string `json:"key"`
}

type jiraType struct {
	Name string `json:"name"`
}

type jiraPriority struct {
	Name string `json:"name"`
}

type jiraIssueResponse struct {
	ID   string `json:"id"`
	Key  string `json:"key"`
	Self string `json:"self"`
}

type jiraIssueDetail struct {
	ID     string              `json:"id"`
	Key    string              `json:"key"`
	Fields jiraIssueFieldsRead `json:"fields"`
}

type jiraIssueFieldsRead struct {
	Summary     string        `json:"summary"`
	Description string        `json:"description"`
	IssueType   jiraType      `json:"issuetype"`
	Priority    *jiraPriority `json:"priority"`
	Status      jiraStatus    `json:"status"`
	Assignee    *jiraUser     `json:"assignee"`
	Reporter    *jiraUser     `json:"reporter"`
	Created     string        `json:"created"`
	Updated     string        `json:"updated"`
}

type jiraStatus struct {
	Name string `json:"name"`
}

type jiraUser struct {
	DisplayName string `json:"displayName"`
	EmailAddress string `json:"emailAddress"`
}

// CreateIssue creates a new Jira issue
func (c *JiraClient) CreateIssue(summary, description, issueType, priority string) (*models.JiraIssue, error) {
	url := fmt.Sprintf("%s/rest/api/2/issue", c.config.BaseURL)

	req := jiraIssueRequest{
		Fields: jiraIssueFields{
			Project: jiraProject{
				Key: c.config.ProjectKey,
			},
			Summary:     summary,
			Description: description,
			IssueType: jiraType{
				Name: issueType,
			},
		},
	}

	if priority != "" {
		req.Fields.Priority = &jiraPriority{Name: priority}
	}

	jsonData, err := json.Marshal(req)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request: %w", err)
	}

	httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	httpReq.SetBasicAuth(c.config.Username, c.config.APIToken)
	httpReq.Header.Set("Content-Type", "application/json")

	resp, err := c.client.Do(httpReq)
	if err != nil {
		return nil, fmt.Errorf("failed to send request: %w", err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)

	if resp.StatusCode != http.StatusCreated {
		c.logger.Error("Failed to create Jira issue",
			zap.Int("status", resp.StatusCode),
			zap.String("body", string(body)))
		return nil, fmt.Errorf("jira API error: %d - %s", resp.StatusCode, string(body))
	}

	var jiraResp jiraIssueResponse
	if err := json.Unmarshal(body, &jiraResp); err != nil {
		return nil, fmt.Errorf("failed to unmarshal response: %w", err)
	}

	c.logger.Info("Created Jira issue",
		zap.String("key", jiraResp.Key),
		zap.String("id", jiraResp.ID))

	// Get full issue details
	return c.GetIssue(jiraResp.Key)
}

// GetIssue retrieves a Jira issue by key
func (c *JiraClient) GetIssue(key string) (*models.JiraIssue, error) {
	url := fmt.Sprintf("%s/rest/api/2/issue/%s", c.config.BaseURL, key)

	httpReq, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	httpReq.SetBasicAuth(c.config.Username, c.config.APIToken)
	httpReq.Header.Set("Accept", "application/json")

	resp, err := c.client.Do(httpReq)
	if err != nil {
		return nil, fmt.Errorf("failed to send request: %w", err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("jira API error: %d - %s", resp.StatusCode, string(body))
	}

	var jiraIssue jiraIssueDetail
	if err := json.Unmarshal(body, &jiraIssue); err != nil {
		return nil, fmt.Errorf("failed to unmarshal response: %w", err)
	}

	return c.convertJiraIssue(&jiraIssue), nil
}

// UpdateIssue updates a Jira issue
func (c *JiraClient) UpdateIssue(key, summary, description string) error {
	url := fmt.Sprintf("%s/rest/api/2/issue/%s", c.config.BaseURL, key)

	updateReq := map[string]interface{}{
		"fields": map[string]interface{}{},
	}

	fields := updateReq["fields"].(map[string]interface{})
	if summary != "" {
		fields["summary"] = summary
	}
	if description != "" {
		fields["description"] = description
	}

	jsonData, err := json.Marshal(updateReq)
	if err != nil {
		return fmt.Errorf("failed to marshal request: %w", err)
	}

	httpReq, err := http.NewRequest("PUT", url, bytes.NewBuffer(jsonData))
	if err != nil {
		return fmt.Errorf("failed to create request: %w", err)
	}

	httpReq.SetBasicAuth(c.config.Username, c.config.APIToken)
	httpReq.Header.Set("Content-Type", "application/json")

	resp, err := c.client.Do(httpReq)
	if err != nil {
		return fmt.Errorf("failed to send request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusNoContent {
		body, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("jira API error: %d - %s", resp.StatusCode, string(body))
	}

	c.logger.Info("Updated Jira issue", zap.String("key", key))
	return nil
}

// TestConnection tests the Jira connection
func (c *JiraClient) TestConnection() error {
	url := fmt.Sprintf("%s/rest/api/2/myself", c.config.BaseURL)

	httpReq, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return fmt.Errorf("failed to create request: %w", err)
	}

	httpReq.SetBasicAuth(c.config.Username, c.config.APIToken)

	resp, err := c.client.Do(httpReq)
	if err != nil {
		return fmt.Errorf("failed to send request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("jira connection test failed: %d - %s", resp.StatusCode, string(body))
	}

	c.logger.Info("Jira connection test successful")
	return nil
}

func (c *JiraClient) convertJiraIssue(jiraIssue *jiraIssueDetail) *models.JiraIssue {
	issue := &models.JiraIssue{
		Key:         jiraIssue.Key,
		ID:          jiraIssue.ID,
		Summary:     jiraIssue.Fields.Summary,
		Description: jiraIssue.Fields.Description,
		IssueType:   jiraIssue.Fields.IssueType.Name,
		Status:      jiraIssue.Fields.Status.Name,
	}

	if jiraIssue.Fields.Priority != nil {
		issue.Priority = jiraIssue.Fields.Priority.Name
	}

	if jiraIssue.Fields.Assignee != nil {
		issue.Assignee = jiraIssue.Fields.Assignee.DisplayName
	}

	if jiraIssue.Fields.Reporter != nil {
		issue.Reporter = jiraIssue.Fields.Reporter.DisplayName
	}

	if created, err := time.Parse(time.RFC3339, jiraIssue.Fields.Created); err == nil {
		issue.Created = created
	}

	if updated, err := time.Parse(time.RFC3339, jiraIssue.Fields.Updated); err == nil {
		issue.Updated = updated
	}

	return issue
}
